package com.bluexmicro.example.page.transaction

import android.annotation.SuppressLint
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCallback
import android.bluetooth.BluetoothProfile
import android.content.Context
import android.widget.Toast
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringSetPreferencesKey
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.bluexmicro.android.coroutineble.model.ConnectionState
import com.bluexmicro.android.ota.OtaParamHelper
import com.bluexmicro.android.ota.OtaTransaction
import com.bluexmicro.android.ota.SingleTransactionTaskCallback
import com.bluexmicro.android.ota.TransactionLaunchCallback
import com.bluexmicro.android.ota.entity.OtaTask
import com.bluexmicro.example.dataStore
import com.bluexmicro.example.page.main.LocalFile
import com.bluexmicro.example.page.main.OtaTaskModel
import com.bluexmicro.example.page.main.TaskStatus
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import java.io.File
import java.math.BigInteger

class TransactionViewModel : ViewModel() {

    val selectedDevice = mutableStateOf<BluetoothDevice?>(null)

    val state = mutableStateOf(TransactionStatus.IDLE)

    val tasks = mutableStateListOf<OtaTaskModel>()

    val updating = mutableStateOf(false)//some one updating

    fun selectFile(context: Context, file: File, address: BigInteger?) {
        val rawData = OtaParamHelper.loadBinData(file) ?: return // todo 告知文件无效
        val taskModel = OtaTaskModel(file, address, rawData)
        if (tasks.isEmpty()) {
            tasks.add(taskModel)
        } else {
            var index = -1
            for (withIndex in tasks.withIndex()) {
                if (withIndex.value.file.path == file.path) {
                    index = withIndex.index
                    break
                }
            }
            if (index in tasks.indices) {
                tasks[index] = taskModel
            } else {
                tasks.add(taskModel)
            }
        }
        saveTasks(context)
    }

    fun removeFile(context: Context, element: OtaTaskModel) {
        tasks.remove(element)
        saveTasks(context)
    }

    fun tapTransaction(context: Context) {
        when (state.value) {
            TransactionStatus.IDLE -> startTransaction(context)
            TransactionStatus.OPENING -> {}
            TransactionStatus.RUNNING -> endTransaction()
        }
    }

    fun endTransaction() = viewModelScope.launch {
        if (state.value == TransactionStatus.RUNNING && !updating.value) {
            setTaskModelStatus(TaskStatus.IDLE, true)
            OtaTransaction.shared().endTransaction(true)
            delay(1000)
            state.value = TransactionStatus.IDLE
        }
    }

    private fun setTaskModelStatus(status: TaskStatus, clearCache: Boolean = false) {
        for (task in tasks) {
            task.status.value = status
            if (clearCache) {
                task.startTimeMillis = 0L
                task.endTimeMillis = 0L
                task.description = ""
                task.progress.value = 0f
                task.errorMessage = null
            }
        }
    }

    private fun startTransaction(context: Context) {
        val device = selectedDevice.value ?: return
        state.value = TransactionStatus.OPENING
        OtaTransaction.shared()
            .startTransaction(context, device, true, object : TransactionLaunchCallback {
                override fun onResult(result: Boolean, errorMessage: String?) {
                    state.value = if (result) TransactionStatus.RUNNING else TransactionStatus.IDLE
                    if (!result) {
                        toast(context, errorMessage ?: "Unknown Error")
                        updating.value = false
                    }
                }
            })
    }

    private fun toast(context: Context, msg: String) = viewModelScope.launch(Dispatchers.Main) {
        Toast.makeText(context, msg, Toast.LENGTH_LONG).show()
    }

    fun execute(taskModel: OtaTaskModel) {
        setTaskModelStatus(TaskStatus.WAITING)
        val task = OtaTask(taskModel.rawData, taskModel.address)
        taskModel.startTimeMillis = System.currentTimeMillis()
        taskModel.endTimeMillis = 0L
        taskModel.description = ""
        taskModel.progress.value = 0f
        taskModel.errorMessage = null
        taskModel.status.value = TaskStatus.UPDATING
        updating.value = true
        OtaTransaction.shared().execute(task, true, object : SingleTransactionTaskCallback {
            override fun onProgress(value: Float) {
                taskModel.progress.value = value
            }

            override fun onResult(result: Boolean, errorMessage: String?) {
                setTaskModelStatus(TaskStatus.IDLE)
                taskModel.endTimeMillis = System.currentTimeMillis()
                taskModel.errorMessage = errorMessage
                taskModel.description = if (errorMessage == null) {
                    val seconds = (taskModel.endTimeMillis - taskModel.startTimeMillis) / 1000
                    ": 成功：耗时${seconds}秒"
                } else {
                    ": 失败：$errorMessage"
                }
                updating.value = false
            }
        })
    }


    ///////////////////////////////////////////////////////////////////////////
    // 策略是：
    // 第一次启动时加载一次
    // 每次更改就保存一次
    ///////////////////////////////////////////////////////////////////////////

    private val taskKey = stringSetPreferencesKey("transaction_tasks")

    private fun loadTasksFlow(context: Context): Flow<List<LocalFile>> =
        context.dataStore.data.map {
            val sets = mutableListOf<LocalFile>()
            it[taskKey]?.forEach { formatTask ->
                //"path|hexAddress?"
                val params = formatTask.split('|')
                val file = if (0 in params.indices) {
                    File(params[0])
                } else {
                    null
                }
                val address = if (1 in params.indices) {
                    try {
                        BigInteger(params[1], 16)
                    } catch (e: Exception) {
                        e.printStackTrace()
                        null
                    }
                } else {
                    null
                }
                if (file != null) {
                    sets.add(LocalFile(file, address))
                }
            }
            sets
        }

    private var hadLoadTask = false
    fun loadLocalTasks(context: Context) = viewModelScope.launch(Dispatchers.IO) {
        if (hadLoadTask) return@launch
        tasks.clear()
        loadTasksFlow(context).firstOrNull()?.let {
            val tmp = mutableListOf<OtaTaskModel>()
            for (local in it) {
                val raw = OtaParamHelper.loadBinData(local.file) ?: continue
                tmp.add(OtaTaskModel(local.file, local.address, raw))
            }
            hadLoadTask = true
            tasks.addAll(tmp)
        }
    }

    private fun saveTasks(context: Context) = viewModelScope.launch(Dispatchers.IO) {
        val list = tasks.map {
            "${it.file.path}|${it.address?.toString(16)}"
        }
        context.dataStore.edit { settings ->
            settings[taskKey] = list.toSet()
        }
    }

}

enum class TransactionStatus {
    IDLE, OPENING, RUNNING
}